#ifndef SOBJC_COMMONS_LIST_H
#define SOBJC_COMMONS_LIST_H

#include <sobjc-commons/port.h>
#include <sobjc-commons/memory.h>
#include <sobjc-commons/bool.h>

#ifdef __cplusplus
extern "C" {
#ifdef 0
} /* for editor */
#endif
#endif 

#define OBJC_LIST_NO_INIT(car)  car
#define OBJC_LIST_NO_FREE(car)

#define OBJC_SIMPLE_DEF_LIST(name,type)     OBJC_DEF_LIST(name,type,type,OBJC_LIST_NO_INIT,OBJC_LIST_NO_FREE)
#define OBJC_DEF_LIST(name,type,intf_type,init_f,free_f)       \
  OBJC_DEF_LIST_TYPE(objc_##name,type)      \
  OBJC_DEF_LIST_FUNCS(name,type,intf_type,init_f,free_f)
 
#define OBJC_DEF_LIST_TYPE(name,type)   \
  typedef struct name {                 \
    type car;                           \
    struct name *cdr;                   \
  } name, *name##_t;

#define OBJC_DEF_LIST_FUNCS(name,type,intf_type,init_f,free_f)   \
  OBJC_DEF_LIST_MAKE(name, objc_##name,type,intf_type,init_f) \
  OBJC_DEF_LIST_FREE(objc_##name,free_f)        \
  OBJC_DEF_LIST_GET_SIZE(objc_##name)           \
  OBJC_DEF_LIST_AT_END(objc_##name)             \
  OBJC_DEF_LIST_GET(objc_##name,intf_type)      \
  OBJC_DEF_LIST_GET_TAIL(objc_##name)           \
  OBJC_DEF_LIST_GET_LAST(objc_##name,intf_type) \
  OBJC_DEF_LIST_CONCATE(objc_##name)            \
  OBJC_DEF_LIST_ADD(name,objc_##name,intf_type) \
  OBJC_DEF_LIST_REMOVE_AT(objc_##name)          \
  OBJC_DEF_LIST_REMOVE(objc_##name,intf_type)   \
  OBJC_DEF_LIST_REMOVE_LAST(objc_##name)        \
  OBJC_DEF_LIST_MAP(objc_##name,intf_type)      \
  OBJC_DEF_LIST_FIND(objc_##name,intf_type)     \
  OBJC_DEF_LIST_FIND_TAIL(objc_##name,intf_type)

#define OBJC_DEF_LIST_MAKE(base,name,type,intf_type,init_f) \
  static __inline__ name *                      \
  objc_make_##base(intf_type _car, name *_cdr)  \
  {                                             \
    name *list;                                 \
                                                \
    list = (name *)objc_malloc(sizeof(name));   \
    list->car = (type)init_f(_car);             \
    list->cdr = _cdr;                           \
                                                \
    return list;                                \
  }
  
#define OBJC_DEF_LIST_FREE(name,free_f)     \
  static __inline__ void            \
  name##_free(name *list)           \
  {                                 \
    name *target;                   \
    while (list != NULL) {          \
      target = list;                \
      list = list->cdr;             \
      free_f(target->car);          \
      objc_free(target);            \
    }                               \
  }

#define OBJC_DEF_LIST_AT_END(name)   \
  static __inline__ BOOL            \
  name##_at_end(name *list)         \
  {                                 \
    return list->cdr == NULL;       \
  }

#define OBJC_DEF_LIST_GET(name,intf_type)   \
  static __inline__ intf_type               \
  name##_get(name *list, int i)             \
  {                                         \
    for (; i > 0; i--)                      \
      list = list->cdr;                     \
    return list->car;                       \
  }

#define OBJC_DEF_LIST_GET_TAIL(name)    \
  static __inline__ name *              \
  name##_tail(name *list, int i)        \
  {                                     \
    for (; i > 0; i--)                  \
      list = list->cdr;                 \
    return list;                        \
  }

#define OBJC_DEF_LIST_GET_LAST(name,intf_type)  \
  static __inline__ intf_type                   \
  name##_get_last(name *list)                   \
  {                                             \
    while (list->cdr != NULL)                   \
      list = list->cdr;                         \
   return list->car;                            \
  }

#define OBJC_DEF_LIST_CONCATE(name)         \
  static __inline__ name *                  \
  name##_concate(name *list1, name *list2)  \
  {                                         \
    if (list1 == NULL)                      \
      return list2;                         \
    else {                                  \
      name *temp = list1;                   \
      while (temp->cdr != NULL)             \
        temp = temp->cdr;                   \
      temp->cdr = list2;                    \
      return list1;                         \
    }                                       \
  }

#define OBJC_DEF_LIST_ADD(base,name,intf_type)  \
  static __inline__ name *                      \
  name##_add(name *list, intf_type _car)        \
  {                                             \
    if (list == NULL)                           \
      return objc_make_##base(_car, NULL);      \
    else {                                      \
      name *temp = list;                        \
      while (temp->cdr != NULL)                 \
        temp = temp->cdr;                       \
      temp->cdr = objc_make_##base(_car, NULL); \
      return list;                              \
    }                                           \
  }                                             \
                                                \
  static __inline__ name *                      \
  name##_add_set(name **list, intf_type _car)   \
  {                                             \
    *list = name##_add(*list, _car);            \
    return *list;                               \
  }

#define OBJC_DEF_LIST_REMOVE_AT(name)           \
  static __inline__ name *                      \
  name##_remove_at(name *list, int i, name **remove) \
  {                                             \
    name *pre = NULL, *temp = list;             \
                                                \
    if (list == NULL)                           \
      return NULL;                              \
                                                \
    if (i == 0) {                               \
      temp = list->cdr;                         \
      if (remove != NULL)                       \
        *remove = list;                         \
      return temp;                              \
    }                                           \
                                                \
    for (; i > 0; i--) {                        \
      pre = temp;                               \
      temp = temp->cdr;                         \
      if (temp == NULL)                         \
        return list;                            \
    }                                           \
    if (temp == NULL) {                         \
      *remove = pre->cdr;                       \
      pre->cdr = NULL;                          \
    } else {                                    \
      pre->cdr = temp->cdr;                     \
      *remove = temp;                           \
    }                                           \
    return list;                                \
  }

#define OBJC_DEF_LIST_REMOVE(name,intf_type)        \
  static __inline__ name *                          \
  name##_remove(name *list, intf_type _car, name **remove)      \
  {                                                 \
    name *temp = list;                              \
    int i = 0;                                      \
                                                    \
    while (temp != NULL) {                          \
      if (temp->car == _car)                        \
        return name##_remove_at(list, i, remove);   \
      temp = temp->cdr;                             \
      i++;                                          \
    }                                               \
    return list;                                    \
  }

#define OBJC_DEF_LIST_REMOVE_LAST(name)                          \
  static __inline__ name *                                      \
  name##_remove_last(name *list, name **remove)                 \
  {                                                             \
    return name##_remove_at(list, name##_get_size(list)-1, remove); \
  }
    
#define OBJC_DEF_LIST_GET_SIZE(name)    \
  static __inline__ size_t              \
  name##_get_size(name *list)           \
  {                                     \
    size_t size = 0;                    \
                                        \
    while (list != NULL) {              \
      size++;                           \
      list = list->cdr;                 \
    }                                   \
                                      \
    return size;                        \
  }

#define OBJC_DEF_LIST_MAP(name,intf_type)           \
  static __inline__ void                            \
  name##_map(name *list, void (*f)(intf_type))      \
  {                                                 \
    while (list != NULL) {                          \
      f(list->car);                                 \
      list = list->cdr;                             \
    }                                               \
  }

#define OBJC_DEF_LIST_FIND(name,intf_type)      \
  static __inline__ BOOL                            \
  name##_find(name *list, BOOL (*f)(intf_type), intf_type *buf)    \
  {                                                 \
    while (list != NULL) {                          \
      if (f(list->car)) {                           \
        *buf = list->car;                           \
        return YES;                                 \
      }                                             \
      list = list->cdr;                             \
    }                                               \
    return NO;                                      \
  }

#define OBJC_DEF_LIST_FIND_TAIL(name,intf_type)     \
  static __inline__ name *                          \
  name##_find_tail(name *list, BOOL (*f)(intf_type))  \
  {                                                 \
    while (list != NULL) {                          \
      if (f(list->car))                             \
        return list;                                \
      list = list->cdr;                             \
    }                                               \
    return NULL;                                    \
  }

#ifdef __cplusplus
}
#endif

#endif /* SOBJC_COMMONS_LIST_H */

